// /////////////////////////////////////////////////////////////////////////////
//
// Name:            Entity.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Represents an object in the game world.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "Entity.h"
#include "GameManager.h"


// Entity class
// Static variables.
u32 Entity::mIDCount = 0;

// Entity constructor.
Entity::Entity(const std::string &name, Entity *parent, AssetGroup *group)
: pParent(NULL), pAssetGroup(group), mName(name), mPosition(0, 0, 0), mIsAddingChild(false)
{
    mID = mIDCount++;

    // Create event slots.
    createEventSlot("onPositionChange");
    createEventSlot("onRotationChange");
    createEventSlot("onUpdate");
    createEventSlot("onRender");
    createEventSlot("onPause");
    createEventSlot("onUnPause");

    // Set parent.
    setParent(parent);
}

// Entity deconstructor.
Entity::~Entity()
{
    // Remove all children, components and parent.
    removeChildren();
    removeComponents();
    removeParent();
}

// Gets the ID of this entity.
u32 Entity::getID() const
{
    return mID;
}

// Gets the name of this entity.
const std::string& Entity::getName() const
{
    return mName;
}

// Adds a child to the entity.
bool Entity::addChild(Entity *entity)
{
    // Are we already adding a child?
    if(mIsAddingChild)
      return true;

    // Did we get a pointer to a entity?
    if(entity == NULL)
      return false;

    // Check if a entity of the given type doesn't already exist.
    if(getChild(entity->getName()))
      return false;

    // Add new component.
    mIsAddingChild = true;
    entity->setParent(this);
    mIsAddingChild = false;

    mChildren.push_back(entity);
    entity->grab();

    return true;
}

// Adds a component to the entity.
bool Entity::addComponent(EntityComponent *component)
{
    // Did we get a pointer to a component?
    if(component == NULL)
      return false;

    // Check if a component of the given type doesn't already exist.
    if(getComponent(component->getName()))
      return false;

    // Add new component.
    mComponents.push_back(component);
    component->grab();

    return true;
}

// Gets pointer to the AssetGroup that is linked to this entity.
AssetGroup* Entity::getAssetGroup()
{
    return pAssetGroup;
}

// Gets the child with the given ID from this entity.
Entity* Entity::getChild(const u32 id)
{
    for(u32 i = 0; i < mChildren.size(); i++)
    {
       if(mChildren[i]->getID() == id)
         return mChildren[i];
    }

    return NULL;
}

// Gets the child with the given name from this entity.
Entity* Entity::getChild(const std::string &name)
{
    for(u32 i = 0; i < mChildren.size(); i++)
    {
       if(mChildren[i]->getName() == name)
         return mChildren[i];
    }

    return NULL;
}

// Gets the component with the given ID from this entity.
EntityComponent* Entity::getComponent(const u32 id)
{
    for(u32 i = 0; i < mComponents.size(); i++)
    {
       if(mComponents[i]->getID() == id)
         return mComponents[i];
    }

    return NULL;
}

// Gets the component with the given name from this entity.
EntityComponent* Entity::getComponent(const std::string &name)
{
    for(u32 i = 0; i < mComponents.size(); i++)
    {
       if(mComponents[i]->getName() == name)
         return mComponents[i];
    }

    return NULL;
}

// Gets the parent of this entity.
Entity* Entity::getParent() const
{
    return pParent;
}

// Gets the absolute position of the entity.
const vector3df& Entity::getAbsolutePosition() const
{
    return mAbsolutePosition;
}

// Gets the position of the entity.
const vector3df& Entity::getPosition() const
{
    return mPosition;
}

// Gets the absolute rotation of the entity.
const vector3df& Entity::getAbsoluteRotation() const
{
    return mAbsoluteRotation;
}

// Gets the rotation of the entity.
const vector3df& Entity::getRotation() const
{
    return mRotation;
}

// Removes all children.
void Entity::removeChildren()
{
    while(mChildren.size() > 0)
    {
       // We don't have to do anything else as the removeParent function calls
       // removeChild internally.
       mChildren[0]->removeParent();
    }
}

// Removes all components.
void Entity::removeComponents()
{
    for(u32 i = 0; i < mComponents.size(); i++)
       mComponents[i]->drop();

    mComponents.clear();
}

// Removes the given child.
bool Entity::removeChild(Entity *entity)
{
    // Did we get a valid pointer?
    if(entity == NULL)
      return false;

    // Try to remove the entity.
    vector<Entity*>::iterator it;

    for(it = mChildren.begin(); it < mChildren.end(); it++)
    {
        Entity *ent = *it;

        if(ent == entity)
        {
           mChildren.erase(it);
           ent->removeParent();
           ent->drop();
           return true;
        }
    }

    // We couldn't find the entity and thus couldn't remove it.
    return false;
}

// Removes the child with the given ID.
bool Entity::removeChild(const u32 id)
{
    // Try to remove the entity.
    vector<Entity*>::iterator it;

    for(it = mChildren.begin(); it < mChildren.end(); it++)
    {
        Entity *ent = *it;

        if(ent->getID() == id)
        {
           mChildren.erase(it);
           ent->removeParent();
           ent->drop();
           return true;
        }
    }

    // We couldn't find the entity and thus couldn't remove it.
    return false;
}

// Removes the child with the given name.
bool Entity::removeChild(const std::string &name)
{
    // Try to remove the entity.
    vector<Entity*>::iterator it;

    for(it = mChildren.begin(); it < mChildren.end(); it++)
    {
        Entity *ent = *it;

        if(ent->getName() == name)
        {
           mChildren.erase(it);
           ent->removeParent();
           ent->drop();
           return true;
        }
    }

    // We couldn't find the entity and thus couldn't remove it.
    return false;
}

// Removes the given component.
bool Entity::removeComponent(EntityComponent *component)
{
    // Did we get a valid pointer?
    if(component == NULL)
      return false;

    // Try to remove the component.
    vector<EntityComponent*>::iterator it;

    for(it = mComponents.begin(); it < mComponents.end(); it++)
    {
        EntityComponent *comp = *it;

        if(comp == component)
        {
           comp->drop();
           mComponents.erase(it);
           return true;
        }
    }

    // We couldn't find the entity and thus couldn't remove it.
    return false;
}

// Removes the component with the given ID.
bool Entity::removeComponent(const u32 id)
{
    // Try to remove the component.
    vector<EntityComponent*>::iterator it;

    for(it = mComponents.begin(); it < mComponents.end(); it++)
    {
        EntityComponent *comp = *it;

        if(comp->getID() == id)
        {
           comp->drop();
           mComponents.erase(it);
           return true;
        }
    }

    // We couldn't find the entity and thus couldn't remove it.
    return false;
}

// Removes the component with the given name.
bool Entity::removeComponent(const std::string &name)
{
    // Try to remove the component.
    vector<EntityComponent*>::iterator it;

    for(it = mComponents.begin(); it < mComponents.end(); it++)
    {
        EntityComponent *comp = *it;

        if(comp->getName() == name)
        {
           comp->drop();
           mComponents.erase(it);
           return true;
        }
    }

    // We couldn't find the entity and thus couldn't remove it.
    return false;
}

// Removes the current parent of this entity.
void Entity::removeParent()
{
    if(pParent != NULL)
    {
       Entity *parent = pParent;
       pParent = NULL;

       parent->removeChild(this);
       parent->drop();

       parent->disconnectEventSignal("onPositionChange", this);
       parent->disconnectEventSignal("onRotationChange", this);
       parent->disconnectEventSignal("onUpdate", this);
       parent->disconnectEventSignal("onRender", this);
       parent->disconnectEventSignal("onPause", this);
       parent->disconnectEventSignal("onUnPause", this);
    }
}

// Sets the absolute position of the entity.
void Entity::setAbsolutePosition(const vector3df &position)
{
    vector3df diff = position - mAbsolutePosition;

    mAbsolutePosition = position;
    mPosition += diff;

    emitEvent("onPositionChange", &diff);
}

// Sets the position of the entity.
void Entity::setPosition(const vector3df &position)
{
    vector3df diff = position - mPosition;

    mPosition = position;
    mAbsolutePosition += diff;

    emitEvent("onPositionChange", &diff);
}

// Sets the parent of the entity.
bool Entity::setParent(Entity *parent)
{
    // Did we get a valid pointer?
    if(parent == NULL)
      return false;

    // Get an extra reference so that this entity doesn't get deleted.
    // Normally this is unneeded, but it's possible that nothing else
    // holds a reference to this entity.
    grab();

    // Remove previous parent.
    removeParent();

    // Add this entity to the parent.
    if(!parent->addChild(this))
    {
       drop();   // Remove extra reference.
       return false;
    }

    pParent = parent;
    pParent->grab();

    // Inherit position and rotation.
    setAbsolutePosition(pParent->getAbsolutePosition());
    setPosition(pParent->getPosition());

    setAbsoluteRotation(pParent->getAbsoluteRotation());
    setRotation(pParent->getRotation());

    // Register events.
    pParent->connectEventSignal("onPositionChange", this, &Entity::onPositionChange);
    pParent->connectEventSignal("onRotationChange", this, &Entity::onRotationChange);
    pParent->connectEventSignal("onUpdate", this, &Entity::onUpdate);
    pParent->connectEventSignal("onRender", this, &Entity::onRender);
    pParent->connectEventSignal("onPause", this, &Entity::onPause);
    pParent->connectEventSignal("onUnPause", this, &Entity::onUnPause);

    // Release extra reference.
    drop();

    return true;
}

// Sets the absolute rotation of the entity.
void Entity::setAbsoluteRotation(const vector3df &rotation)
{
    vector3df diff = rotation - mAbsoluteRotation;

    mAbsoluteRotation = rotation;
    mRotation += diff;

    emitEvent("onRotationChange", &diff);
}

// Sets the rotation of the entity.
void Entity::setRotation(const vector3df &rotation)
{
    vector3df diff = rotation - mRotation;

    mRotation = rotation;
    mAbsoluteRotation += diff;

    emitEvent("onRotationChange", &diff);
}

// Updates the position of the entity after its parent has been updated.
void Entity::onPositionChange(void *p)
{
    vector3df diff = reinterpret_cast<vector3df*>(p)[0];

    mAbsolutePosition += diff;
    mPosition += diff;

    emitEvent("onPositionChange", &diff);
}

// Updates the rotation of the entity after its parent has been updated.
void Entity::onRotationChange(void *p)
{
    vector3df diff = reinterpret_cast<vector3df*>(p)[0];

    mAbsoluteRotation += diff;
    mRotation += diff;

    emitEvent("onRotationChange", &diff);
}

// Updates the entity if the parent is updated.
void Entity::onUpdate(void *p)
{
    emitEvent("onUpdate", p);
}

// Renders the entity if the parent is rendered.
void Entity::onRender(void *p)
{
    emitEvent("onRender", p);
}

// Pauses the entity if the parent is paused.
void Entity::onPause(void *p)
{
    emitEvent("onPause", p);
}

// Updates the entity if the parent is unpaused.
void Entity::onUnPause(void *p)
{
    emitEvent("onUnPause", p);
}

// Serializes this object.
bool Entity::onSerialization(SerializedAttributes *attributes)
{
    // Create a root.
    SerializedAttributes *root = new SerializedAttributes();

    // Serialize name.
    root->pushString("name", mName);

    // Serialize children.
    SerializedAttributes *children = new SerializedAttributes();

    vector<Entity*>::iterator itChildren;
    for(itChildren = mChildren.begin(); itChildren < mChildren.end(); itChildren++)
      children->pushObject("", *itChildren);

    root->pushObject("Children", children);
    children->drop();

    // Serialize components.
    SerializedAttributes *components = new SerializedAttributes();

    vector<EntityComponent*>::iterator itComponents;
    for(itComponents = mComponents.begin(); itComponents < mComponents.end(); itComponents++)
      components->pushObject("", *itComponents);

    root->pushObject("Components", components);
    components->drop();

    // Add root to the given collection of attributes.
    attributes->pushObject("Entity", root);
    root->drop();

    // Return
    return true;
}

// Deserializes this object.
bool Entity::onDeserialization(SerializedAttributes *attributes)
{
    // Retrieve root attribute from the collection.
    SerializedAttribute *rootAttribute = attributes->pullAttribute();

    if(rootAttribute == NULL || rootAttribute->getName() != "Entity")
      return false;

    SerializedAttributes *root = reinterpret_cast<SerializedAttributes*>(rootAttribute->getObject());
    root->grab();

    rootAttribute->drop(); // We no longer need this attribute.

    // Process all attributes in the root collection.
    for(SerializedAttribute *attribute = root->pullAttribute(); attribute != NULL; attribute = root->pullAttribute())
    {
       // Name attribute
       if(attribute->getName() == "name")
         mName = attribute->getString();

       // Size attribute
       else if(attribute->getName() == "Components")
       {
          // Convert to a collection of attributes.
          SerializedAttributes *components = reinterpret_cast<SerializedAttributes*>(attribute->getObject());

          // Process all attributes in the components collection.
          for(SerializedAttribute *innerAttribute = components->pullAttribute(); innerAttribute != NULL; innerAttribute = components->pullAttribute())
          {
             // Try to create a component of the given type.
             EntityComponent *component = createComponent(innerAttribute->getName(), this);

             // Did we succesfully create a component?
             if(component != NULL)
             {
                // Rewrap attribute so that it can be deserialized properly.
                SerializedAttributes *wrappedComponent = new SerializedAttributes();
                wrappedComponent->pushAttribute(innerAttribute);

                // Deserialize the component.
                component->onDeserialization(wrappedComponent);

                // Clean up.
                wrappedComponent->drop();
             }

             // Clean up.
             component->drop();
          }
       }

       // We no longer need a reference to the attribute.
       attribute->drop();
    }

    // We no longer need a reference to the root.
    root->drop();

    // We're done.
    return true;
}

// End of File
